package com.ds.infrastructure.hyperspace.console.controller;

import com.ds.infrastructure.hyperspace.console.dto.configdata.ConfigDataDTO;
import com.ds.infrastructure.hyperspace.console.entity.enums.TbNodeMetricsEnum;
import com.ds.infrastructure.hyperspace.console.entity.table.TbNodeMetrics;
import com.ds.infrastructure.hyperspace.console.model.request.monitor.GetMetricsForm;
import com.ds.infrastructure.hyperspace.console.model.response.diagram.Column10Chart;
import com.ds.infrastructure.hyperspace.console.model.response.diagram.HeatMapChart;
import com.ds.infrastructure.hyperspace.console.model.response.diagram.SeriesChart;
import com.ds.infrastructure.hyperspace.console.model.response.monitor.GetMetricsVO;
import com.ds.infrastructure.hyperspace.console.model.response.result.JsonResultData;
import com.ds.infrastructure.hyperspace.console.model.response.result.JsonResultList;
import com.ds.infrastructure.hyperspace.console.service.inf.IContainerActiveConfigService;
import com.ds.infrastructure.hyperspace.console.service.inf.IMonitorManageService;
import com.ds.infrastructure.hyperspace.console.utils.ListUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.http.client.fluent.Request;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.stream.Collectors;

import static com.ds.infrastructure.hyperspace.console.dto.configdata.ConfigDataDTO.ENDPOINT_TYPE.TARGET;

/**
 * @author: yaozhendong
 * @create: 2019-11-04 14:02
 **/
@RestController
@RequestMapping(value = "/monitor", produces = {"application/json;charset=UTF-8"})
@Api(value = "Manager Monitor Controller", tags = "Monitor管理")
public class MonitorController {
    @Autowired
    IMonitorManageService iMonitorManageService;
    @Autowired
    IContainerActiveConfigService iContainerActiveConfigService;
    private static final int PAGE_SIZE = 16;

    @PostMapping(value = "/getMetrics")
    @ApiOperation(value = "获取-监控指标")
    public JsonResultData<GetMetricsVO> getMetrics(@RequestBody @Valid GetMetricsForm form) {
        String serviceName = ConfigDataDTO.getNamespace(form.getServiceName(), TARGET.value);
        GetMetricsVO getMetricsVO = new GetMetricsVO();
        Map<String, List<TbNodeMetrics>> mapMetrics = iMonitorManageService.getMetrics(form.getStageId(), serviceName, form.getBeginTime(), form.getEndTime(), PAGE_SIZE);
        //Gauge 连接
        List<SeriesChart> gatewayConnectionCountMetricsVO = toVO4GatewayConnectionCountMetrics(mapMetrics, serviceName);
        getMetricsVO.setGatewayConnectionCountMetrics(gatewayConnectionCountMetricsVO);
        //Gauge 堆积
        List<SeriesChart> gatewayRequestsPendingMetricsVO = toVO4GatewayRequestsPendingMetrics(mapMetrics, serviceName);
        getMetricsVO.setGatewayRequestsPendingMetrics(gatewayRequestsPendingMetricsVO);
        //Counter
        List<SeriesChart> gatewayRequestsCountMetricsVO = toVO4GatewayRequestsCountMetrics(mapMetrics);
        getMetricsVO.setGatewayRequestsCountMetrics(gatewayRequestsCountMetricsVO);
        //Counter
        List<SeriesChart> gatewayPayloadBytesMetricsVO = toVO4GatewayPayloadBytesMetrics(mapMetrics, serviceName);
        getMetricsVO.setGatewayPayloadBytesMetrics(gatewayPayloadBytesMetricsVO);
        //响应时间分布
        List<Column10Chart> responseDurationMetricsVO = toVO4ResponseDurationMetrics(mapMetrics);
        getMetricsVO.setResponseDurationMetrics(responseDurationMetricsVO);
        //错误请求状态码分布
        List<Column10Chart> errorRequestCountMetricsVO = toVO4ErrorRequestCountMetrics(mapMetrics);
        getMetricsVO.setErrorRequestCountMetrics(errorRequestCountMetricsVO);
        //
        List<HeatMapChart> targetNodeStatusMetricsVO = toVO4TargetNodeStatusMetrics(mapMetrics);
        getMetricsVO.setTargetNodeStatusMetrics(targetNodeStatusMetricsVO);
        //
        return JsonResultData.build(getMetricsVO);
    }

    private List<HeatMapChart> toVO4TargetNodeStatusMetrics(Map<String, List<TbNodeMetrics>> mapMetrics) {
        List<TbNodeMetrics> targetNodeStatusMetrics = mapMetrics.get(TbNodeMetricsEnum.METRICS_KEY.HYPERSPACE_TARGET_NODE_STATUS.key);
        if (targetNodeStatusMetrics == null) {
            return Collections.emptyList();
        }
        List<HeatMapChart> heatMapChartList = new ArrayList<>();
        targetNodeStatusMetrics.forEach(item -> heatMapChartList.add(HeatMapChart.toVO(item)));
        return heatMapChartList;
    }

    private List<Column10Chart> toVO4ErrorRequestCountMetrics(Map<String, List<TbNodeMetrics>> mapMetrics) {
        List<TbNodeMetrics> errorRequestCountMetrics = mapMetrics.get(TbNodeMetricsEnum.METRICS_KEY.HYPERSPACE_REQUESTS_TOTAL.key);
        if (errorRequestCountMetrics == null) {
            return Collections.emptyList();
        }
        List<Column10Chart> column10ChartList = new ArrayList<>();
        List<Column10Chart> gatewayErrorRequestCount = statisticsGatewayErrorRequestCount(errorRequestCountMetrics);
        column10ChartList.addAll(gatewayErrorRequestCount);
        //
        List<Column10Chart> targetErrorRequestCount = statisticsTargetErrorRequestCount(errorRequestCountMetrics);
        column10ChartList.addAll(targetErrorRequestCount);
        return column10ChartList;
    }

    private List<Column10Chart> statisticsTargetErrorRequestCount(List<TbNodeMetrics> errorRequestCountMetrics) {
        List<TbNodeMetrics> targetErrorRequestCountMetricsSource = errorRequestCountMetrics.stream()
                .filter(item -> Integer.parseInt(item.getServiceExtend2()) >= 400).collect(Collectors.toList());
        if (targetErrorRequestCountMetricsSource == null) {
            return Collections.emptyList();
        }
        List<TbNodeMetrics> targetErrorRequestCountMetrics = ListUtil.deepCopy(targetErrorRequestCountMetricsSource, TbNodeMetrics.class);
        String code4xx = "4XX";
        String code5xx = "5XX";
        String codeOther = "Other";
        for (TbNodeMetrics errorRequestCountMetric : targetErrorRequestCountMetrics) {
            if (errorRequestCountMetric.getServiceExtend2().indexOf('4') == 0) {
                errorRequestCountMetric.setServiceExtend2(code4xx);
                continue;
            }
            if (errorRequestCountMetric.getServiceExtend2().indexOf('5') == 0) {
                errorRequestCountMetric.setServiceExtend2(code5xx);
                continue;
            }
            errorRequestCountMetric.setServiceExtend2(codeOther);
        }
        List<Column10Chart> column10ChartsList = new ArrayList<>();
        List<TbNodeMetrics> code4xxErrorRequestCountMetrics = targetErrorRequestCountMetrics.stream().filter(item -> code4xx.equals(item.getServiceExtend2())).collect(Collectors.toList());
        column10ChartsList.addAll(toColumn10Chart(code4xxErrorRequestCountMetrics, TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.TARGET.key + "-" + code4xx));
        //
        List<TbNodeMetrics> code5xxErrorRequestCountMetrics = targetErrorRequestCountMetrics.stream().filter(item -> code5xx.equals(item.getServiceExtend2())).collect(Collectors.toList());
        column10ChartsList.addAll(toColumn10Chart(code5xxErrorRequestCountMetrics, TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.TARGET.key + "-" + code5xx));
        //
        List<TbNodeMetrics> codeOtherErrorRequestCountMetrics = targetErrorRequestCountMetrics.stream().filter(item -> codeOther.equals(item.getServiceExtend2())).collect(Collectors.toList());
        column10ChartsList.addAll(toColumn10Chart(codeOtherErrorRequestCountMetrics, TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.TARGET.key + "-" + codeOther));
        return column10ChartsList;

    }

    private List<Column10Chart> statisticsGatewayErrorRequestCount(List<TbNodeMetrics> errorRequestCountMetrics) {
        List<TbNodeMetrics> gatewayErrorRequestCountMetricsSource = errorRequestCountMetrics.stream()
                .filter(item -> "-1".equals(item.getServiceExtend2())).collect(Collectors.toList());
        if (gatewayErrorRequestCountMetricsSource == null) {
            return Collections.emptyList();
        }
        List<TbNodeMetrics> gatewayErrorRequestCountMetrics = ListUtil.deepCopy(gatewayErrorRequestCountMetricsSource, TbNodeMetrics.class);
        String code4xx = "4XX";
        String code5xx = "5XX";
        String codeOther = "Other";
        for (TbNodeMetrics gatewayErrorRequestCountMetric : gatewayErrorRequestCountMetrics) {
            if (gatewayErrorRequestCountMetric.getServiceExtend1().indexOf('4') == 0) {
                gatewayErrorRequestCountMetric.setServiceExtend1(code4xx);
                continue;
            }
            if (gatewayErrorRequestCountMetric.getServiceExtend1().indexOf('5') == 0) {
                gatewayErrorRequestCountMetric.setServiceExtend1(code5xx);
                continue;
            }
            gatewayErrorRequestCountMetric.setServiceExtend1(codeOther);
        }
        List<Column10Chart> column10ChartsList = new ArrayList<>();
        List<TbNodeMetrics> code4xxErrorRequestCountMetrics = gatewayErrorRequestCountMetrics.stream().filter(item -> code4xx.equals(item.getServiceExtend1())).collect(Collectors.toList());
        column10ChartsList.addAll(toColumn10Chart(code4xxErrorRequestCountMetrics, TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.GATEWAY.key + "-" + code4xx));
        //
        List<TbNodeMetrics> code5xxErrorRequestCountMetrics = gatewayErrorRequestCountMetrics.stream().filter(item -> code5xx.equals(item.getServiceExtend1())).collect(Collectors.toList());
        column10ChartsList.addAll(toColumn10Chart(code5xxErrorRequestCountMetrics, TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.GATEWAY.key + "-" + code5xx));
        //
        List<TbNodeMetrics> codeOtherErrorRequestCountMetrics = gatewayErrorRequestCountMetrics.stream().filter(item -> codeOther.equals(item.getServiceExtend1())).collect(Collectors.toList());
        column10ChartsList.addAll(toColumn10Chart(codeOtherErrorRequestCountMetrics, TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.GATEWAY.key + "-" + codeOther));
        return column10ChartsList;
    }

    private List<Column10Chart> toVO4ResponseDurationMetrics(Map<String, List<TbNodeMetrics>> mapMetrics) {
        List<TbNodeMetrics> responseDurationMetrics = mapMetrics.get(TbNodeMetricsEnum.METRICS_KEY.HYPERSPACE_REQUEST_SECONDS_BUCKET.key);
        if (responseDurationMetrics == null) {
            return Collections.emptyList();
        }
        //
        List<Column10Chart> columnChartsList = new ArrayList<>();
        List<TbNodeMetrics> gatewayResponseDurationMetrics = responseDurationMetrics.stream()
                .filter(item -> TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.GATEWAY.key.equals(item.getServiceExtend1())).collect(Collectors.toList());
        statisticsResponseDurationMetrics(TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.GATEWAY, gatewayResponseDurationMetrics, columnChartsList);
        List<TbNodeMetrics> targetResponseDurationMetrics = responseDurationMetrics.stream()
                .filter(item -> TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.TARGET.key.equals(item.getServiceExtend1())).collect(Collectors.toList());
        statisticsResponseDurationMetrics(TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.TARGET, targetResponseDurationMetrics, columnChartsList);
        return columnChartsList;
    }

    private void statisticsResponseDurationMetrics(TbNodeMetricsEnum.ENDPOINT_TYPE_TAG endpointTypeTag, List<TbNodeMetrics> responseDurationMetrics, List<Column10Chart> columnChartsList) {
        for (TbNodeMetricsEnum.HYPERSPACE_REQUEST_SECONDS_BUCKET_LE_TAG leTag : TbNodeMetricsEnum.HYPERSPACE_REQUEST_SECONDS_BUCKET_LE_TAG.values()) {
            List<TbNodeMetrics> responseDurationMetrics4LeTag = responseDurationMetrics.stream()
                    .filter(item -> leTag.key.equals(item.getServiceExtend2())).collect(Collectors.toList());
            columnChartsList.addAll(toColumn10Chart(responseDurationMetrics4LeTag, endpointTypeTag.key + "-" + leTag.key));
        }
    }

    /**
     * 流量
     *
     * @param mapMetrics
     * @param serviceName
     * @return
     */
    private List<SeriesChart> toVO4GatewayPayloadBytesMetrics(Map<String, List<TbNodeMetrics>> mapMetrics, String serviceName) {
        List<TbNodeMetrics> payloadBytesMetrics = mapMetrics.get(TbNodeMetricsEnum.METRICS_KEY.HYPERSPACE_PAYLOAD_BYTES.key);
        if (payloadBytesMetrics == null) {
            return Collections.emptyList();
        }
        return toSeriesChart(payloadBytesMetrics, serviceName);
    }

    /**
     * 请求数
     *
     * @param mapMetrics
     * @return
     */
    private List<SeriesChart> toVO4GatewayRequestsCountMetrics(Map<String, List<TbNodeMetrics>> mapMetrics) {
        List<TbNodeMetrics> requestTotalMetricsSource = mapMetrics.get(TbNodeMetricsEnum.METRICS_KEY.HYPERSPACE_REQUESTS_TOTAL.key);
        if (requestTotalMetricsSource == null) {
            return Collections.emptyList();
        }
        List<TbNodeMetrics> requestTotalMetrics = ListUtil.deepCopy(requestTotalMetricsSource, TbNodeMetrics.class);
        String successTag = "SUCCESS";
        String gatewayFailTag = "GATEWAY FAIL";
        String targetFailTag = "TARGET FAIL";
        for (TbNodeMetrics requestTotalMetric : requestTotalMetrics) {
            int httpCode = Integer.parseInt(requestTotalMetric.getServiceExtend2());
            if (httpCode > 0 && httpCode < 400) {
                requestTotalMetric.setServiceExtend2(successTag);
                continue;
            }
            if (-1 == httpCode) {
                requestTotalMetric.setServiceExtend2(gatewayFailTag);
                continue;
            }
            requestTotalMetric.setServiceExtend2(targetFailTag);
        }
        //
        List<SeriesChart> seriesChartList = new ArrayList<>();
        List<TbNodeMetrics> requestSuccessTotalMetrics = requestTotalMetrics.stream().filter(item -> successTag.equals(item.getServiceExtend2())).collect(Collectors.toList());
        seriesChartList.addAll(toSeriesChart(requestSuccessTotalMetrics, successTag));
        List<TbNodeMetrics> requestGatewayFailTotalMetrics = requestTotalMetrics.stream().filter(item -> gatewayFailTag.equals(item.getServiceExtend2())).collect(Collectors.toList());
        seriesChartList.addAll(toSeriesChart(requestGatewayFailTotalMetrics, gatewayFailTag));
        List<TbNodeMetrics> requestTargetFailTotalMetrics = requestTotalMetrics.stream().filter(item -> targetFailTag.equals(item.getServiceExtend2())).collect(Collectors.toList());
        seriesChartList.addAll(toSeriesChart(requestTargetFailTotalMetrics, targetFailTag));
        return seriesChartList;
    }

    /**
     * 堆积数
     *
     * @param mapMetrics
     * @param serviceName
     * @return
     */
    private List<SeriesChart> toVO4GatewayRequestsPendingMetrics(Map<String, List<TbNodeMetrics>> mapMetrics, String serviceName) {
        List<TbNodeMetrics> pendingMetrics = mapMetrics.get(TbNodeMetricsEnum.METRICS_KEY.HYPERSPACE_REQUESTS_PENDING.key);
        if (pendingMetrics == null) {
            return Collections.emptyList();
        }
        return toSeriesChart(pendingMetrics, serviceName);
    }

    /**
     * 连接数
     *
     * @param mapMetrics
     * @param serviceName
     * @return
     */
    private List<SeriesChart> toVO4GatewayConnectionCountMetrics(Map<String, List<TbNodeMetrics>> mapMetrics, String serviceName) {
        List<TbNodeMetrics> connectionCountMetrics = mapMetrics.get(TbNodeMetricsEnum.METRICS_KEY.HYPERSPACE_CONNECTIONS.key);
        if (connectionCountMetrics == null) {
            return Collections.emptyList();
        }
        List<TbNodeMetrics> nodeMetricsList = connectionCountMetrics.stream().filter(item ->
                TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.TARGET.key.equals(item.getServiceExtend1())).collect(Collectors.toList());
        return toSeriesChart(nodeMetricsList, serviceName);
    }

    private List<SeriesChart> toSeriesChart(List<TbNodeMetrics> tbNodeMetrics, String serviceName) {
        Map<Long, LongSummaryStatistics> summaryStatisticsMap = tbNodeMetrics.stream().collect(Collectors.groupingBy(TbNodeMetrics::getExecuteTime, Collectors.summarizingLong(TbNodeMetrics::getMetricsRealValue)));
        List<SeriesChart> seriesChartList = new ArrayList<>();
        for (Map.Entry<Long, LongSummaryStatistics> entry : summaryStatisticsMap.entrySet()) {
            seriesChartList.add(SeriesChart.toVO(entry, serviceName));
        }
        return seriesChartList;
    }

    private List<Column10Chart> toColumn10Chart(List<TbNodeMetrics> tbNodeMetrics, String serviceName) {
        Map<Long, LongSummaryStatistics> summaryStatisticsMap = tbNodeMetrics.stream().collect(Collectors.groupingBy(TbNodeMetrics::getExecuteTime, Collectors.summarizingLong(TbNodeMetrics::getMetricsRealValue)));
        List<Column10Chart> column10ChartList = new ArrayList<>();
        for (Map.Entry<Long, LongSummaryStatistics> entry : summaryStatisticsMap.entrySet()) {
            column10ChartList.add(Column10Chart.toVO(entry, serviceName));
        }
        return column10ChartList;
    }

    @GetMapping(value = "/getProxyNameList")
    @ApiOperation(value = "获得-代理列表")
    public JsonResultList<String> getProxyNameList(@RequestParam(name = "stageId") Long stageId) {
        List<String> proxyNameList = iMonitorManageService.getProxyNameList(stageId);
        return JsonResultList.build(proxyNameList);
    }

    @GetMapping(value = "/getGatewayOriginMetrics")
    @ApiOperation(value = "获得-网关原始指标数据")
    public String getGatewayOriginMetrics(@RequestParam(name = "target") String target) throws IOException, URISyntaxException {
        URI uri = new URI("http://" + target);
        return Request.Get(uri).connectTimeout(1000).execute().returnContent().asString();
    }
}
